package com.blockmeta.bbs.businesslibrary.launch;

import android.content.res.AssetManager;
import android.net.Uri;
import android.text.TextUtils;
import com.blankj.utilcode.util.LogUtils;

import com.blankj.utilcode.util.LogUtils;
import com.blockmeta.bbs.baselibrary.base.app.BaseApp;
import com.blockmeta.bbs.baselibrary.utils.SecurityUtil;
import com.blockmeta.bbs.baselibrary.utils.SharedPreferenceUtil;
import com.blockmeta.bbs.businesslibrary.constant.Constant;
import com.blockmeta.bbs.businesslibrary.net.retrofit.RestManager;
import com.blockmeta.bbs.businesslibrary.net.retrofit.api.InterfaceSys;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;


public class BundleManager {
    private static BundleManager m_bundleManager;
    private final String m_strFilePath = BaseApp.getApp().getFilesDir().getAbsolutePath() + File.separator;
    private final String m_strBundleVersionName = "version.file";
    private String m_strPackage = this.getClass().getPackage().getName();
    private final String m_strBundleName = "bundle";

    public static BundleManager getInstance() {
        if (m_bundleManager == null) {
            m_bundleManager = new BundleManager();
        }
        return m_bundleManager;
    }

    public Map<String, Object> m_bundleMap = new HashMap<String, Object>();

    private FileFilter m_filterDir = new FileFilter() {
        @Override
        public boolean accept(File pathname) {
            return pathname.isDirectory();
        }
    };

    private FileFilter m_filterYml = new FileFilter() {
        @Override
        public boolean accept(File pathname) {
            String name = pathname.getName();
            return !pathname.isDirectory() && !pathname.getName().startsWith(".") && (name.endsWith(".yml") || name.endsWith(".yaml") || name.endsWith(".ymal") || name.endsWith(".json"));
        }
    };


    public BundleManager() {
    }

    void generateMapping(String env) {
        parserProductBundle(env);
    }


    private void parserProductBundle(String env) {
        File prodFile = getFilePath(m_strBundleName + File.separator);
        File[] fileList = prodFile.listFiles(m_filterYml);
        for (File yml : fileList) {
            Map<String, Object> map = parserYml(yml.getName(), getBundleContent(yml), env);

            m_bundleMap.putAll(map);
        }
    }

    File getFilePath() {
        return getFilePath("");
    }

    File getFilePath(String path) {
        File filePath;
        if (!TextUtils.isEmpty(path)) {
            filePath = new File(m_strFilePath + path + File.separator);
        } else {
            filePath = new File(m_strFilePath);
        }

        if (!filePath.exists()) {
            filePath.mkdirs();
        }
        return filePath;
    }

    String getBundleContent(File file) {
        String res = "";
        if (file.exists()) {
            StringBuffer fileData = new StringBuffer("");
            BufferedReader reader = null;
            try {
                reader = new BufferedReader(new FileReader(file));

                char[] buf = new char[1024];
                int numRead = 0;
                while ((numRead = reader.read(buf)) != -1) {
                    String readData = String.valueOf(buf, 0, numRead);
                    fileData.append(readData);
                }
                reader.close();
            } catch (FileNotFoundException e1) {
                e1.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                CloseUtil.closeQuietly(reader);
            }
            return fileData.toString();
        }
        return res;
    }

    String getBundleVersion() {
        String bundlezVersion = SharedPreferenceUtil.getString(BaseApp.getApp(), Constant.BUNDLEVERSION, "");
        return bundlezVersion;
//        return getBundleContent(new File(m_strFilePath + m_strBundleName + File.separator + m_strBundleVersionName));
    }

    void putBundleVersion(String newVerdion) {
        SharedPreferenceUtil.putString(BaseApp.getApp(), Constant.BUNDLEVERSION, newVerdion);
    }


    private Map<String, Object> parserYml(String name, String content, String env) {
        int slash = name.lastIndexOf('/');
        if (slash != -1) {
            name = name.substring(slash + 1);
        }
        int dot = name.indexOf('.');
        name = ((dot == -1 ? name : name.substring(0, dot)) + "YMAL");

        IBaseYMAL ymalClass;
        try {
            ymalClass = (IBaseYMAL) newInstance(m_strPackage, name);
            return ymalClass.parser(content, env);
        } catch (Exception e) {

        }

        ymalClass = new BaseYAML();
        return ymalClass.parser(content, env);
    }

    void copyBundleFile() {
        AssetManager assetManager = BaseApp.getApp().getAssets();
        clearFiles();
        copyBundleFile(assetManager, m_strBundleName);
    }

    public void clearFiles() {
        File bundleFile = new File(getFilePath() + File.separator + m_strBundleName + File.separator);
        if (bundleFile.exists()) {
            deleteDirWihtFile(bundleFile);
            bundleFile.mkdirs();
        }
    }

    /**
     * 递归删除文件和文件夹
     * <p>
     * 要删除的根目录
     */
    public static void deleteDir(final String pPath) {
        File dir = new File(pPath);
        deleteDirWihtFile(dir);
    }

    public static void deleteDirWihtFile(File dir) {
        if (dir == null || !dir.exists() || !dir.isDirectory())
            return;
        for (File file : dir.listFiles()) {
            if (file.isFile())
                file.delete(); // 删除所有文件
            else if (file.isDirectory())
                deleteDirWihtFile(file); // 递规的方式删除文件夹
        }
        dir.delete();// 删除目录本身
    }


    private void copyBundleFile(AssetManager manager, String assetName) {
        try {
            String[] files = manager.list(assetName);
            for (String fileName : files) {
                String name = fileName.toLowerCase();
                String assetFileName = assetName + File.separator + fileName;
                if (name.endsWith(".file") || name.endsWith(".yml") || name.endsWith(".ymal") || name.endsWith(".yaml") || name.endsWith(".json")) {
                    copyBundleFile(manager, assetFileName, m_strFilePath + assetFileName);
                } else {
                    copyBundleFile(manager, assetFileName);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void copyBundleFile(AssetManager manager, String fileName, String target) {
        File targetFile = new File(target);
        if (targetFile.isDirectory()) {
            if (!targetFile.exists()) {
                targetFile.mkdirs();
            }
        } else {
            if (!targetFile.getParentFile().exists()) {
                targetFile.getParentFile().mkdirs();
            }
        }
        LogUtils.d(BundleManager.getInstance().m_strBundleName, fileName);
        InputStream in = null;
        OutputStream out = null;
        try {
            in = manager.open(fileName);
            out = new FileOutputStream(targetFile);

            byte[] buffer = new byte[1024];
            int read;
            while ((read = in.read(buffer)) != -1) {
                out.write(buffer, 0, read);
            }
        } catch (IOException e) {
        } finally {
            CloseUtil.closeQuietly(in);
            CloseUtil.closeQuietly(out);
        }
    }


    public void downloadBundleZip(String url, final DownloadInfo.DownLoadCallback downLoadCallback) {

        InterfaceSys sysService = RestManager.getRetrofit().create(InterfaceSys.class);
        Uri uri = Uri.parse(url);
        String urlNoHost = uri.getPath();
        String token = SharedPreferenceUtil.getString(BaseApp.getApp(), Constant.TOKEN,"");
        String time = String.valueOf(System.currentTimeMillis()/1000);
        String sign = SecurityUtil.MD5(urlNoHost+token+time);
        Call<ResponseBody> call = sysService.downloadZipFile(url,sign,time);

        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                if (response.isSuccessful()) {
                    LogUtils.d("bundle", "server contacted and has file");

                    boolean writtenToDisk = false;
                    try {
                        writtenToDisk = writeResponseBodyToDisk(response.body().bytes());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                    if (writtenToDisk) {
                        downLoadCallback.onSuccess();
                        LogUtils.d("bundle", "download write success");
                    } else {
                        downLoadCallback.onError();
                    }


                }


            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                LogUtils.e("bundle", "error");
            }
        });


    }

    private boolean writeResponseBodyToDisk(byte[] bytes) {

        byte[] buffer = new byte[1024];

        try {
            InputStream is = new ByteArrayInputStream(bytes);
            ZipInputStream zis = new ZipInputStream(is);
            ZipEntry ze = zis.getNextEntry();
            FileOutputStream fos = null;

            while (ze != null) {
                if (ze.isDirectory()) {

                } else {

                    String fileName = ze.getName();
                    fileName = fileName.substring(fileName.lastIndexOf(File.separator) + 1);
                    LogUtils.e("bundle", "unzipFileName : " + fileName);
                    File outFile = new File(m_strFilePath + m_strBundleName + File.separator + fileName);
//					File outFile = getFilePath(m_strBundleName + File.separator + fileName);
                    if (!outFile.getParentFile().exists()) {
                        outFile.getParentFile().mkdirs();
                    }
                    fos = new FileOutputStream(outFile);

                    int len;
                    while ((len = zis.read(buffer)) > 0) {
                        fos.write(buffer, 0, len);
                    }
                    CloseUtil.closeQuietly(fos);
                }


                ze = zis.getNextEntry();

            }

            zis.closeEntry();
            CloseUtil.closeQuietly(zis);
        } catch (IOException ex) {
            ex.printStackTrace();
            return false;
        }
        return true;
    }


    public static Object newInstance(String packageName, String className) throws Exception {
        try {
            String name = packageName + "." + className;
            Class<?> tClass = Class.forName(name);
            Constructor<?> c = tClass.getConstructor();
            return c.newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        throw new Exception(packageName + "." + className + " Fail");
    }
}
